<?php

namespace Biz\service\adCalendar;

use Biz\model\advertisement\MPositionAdRule;
use Biz\model\advertisement\MPositionAdRuleDetail;
use Doraemon\tools\Log;

/**
 * 规则支持设置强制共享、强制不共享 √
 * 规则支持设置支持最多同时有n个使用部门 √
 * 规则支持设置共享数量上限n √
 * 规则支持互斥关键词
 * Class AdPositionRules
 * @package Doraemon\service
 */
class AdPositionRules
{

    /**
     * 通过资源位id获取对应的配置规则
     * @param $adTypeId
     * @return array|mixed
     */
    public static function getRulesByAdType($adTypeId)
    {
        $condition = empty($adTypeId) ? ['is_deleted' => MPositionAdRuleDetail::IS_DELETED_NO] : ['ad_type_id' => $adTypeId, 'is_deleted' => MPositionAdRuleDetail::IS_DELETED_NO];
        $ad_rule = MPositionAdRuleDetail::select('*', $condition);
        $ad_rule_map = [];
        $ad_rule_ids = [];
        if ($ad_rule) {
            foreach ($ad_rule as $v) {
                $ad_rule_map[$v['ad_type_id']][] = $v['ad_rule_id'];
                $ad_rule_ids[$v['ad_rule_id']] = $v['ad_rule_id'];
            }
        }

        $rules = empty($ad_rule_ids) ? [] : MPositionAdRule::select('*', ['id' => $ad_rule_ids, 'is_deleted' => MPositionAdRule::IS_DELETED_NO]);
        $rules = empty($rules) ? [] : array_column($rules, null, 'id');

        $response = [];
        foreach ($ad_rule_map as $id => $v) {
            foreach ($v as $i) {
                $temp = $rules[$i] ?? [];
                if (isset($response[$id][$temp['rule_type']])) {
                    array_push($response[$id][$temp['rule_type']], $temp['rule_value']);
                } else {
                    $response[$id][$temp['rule_type']] = [$temp['rule_value']];
                }
            }
        }

        return is_scalar($adTypeId) ? $response[$adTypeId] : $response;
    }

    /**
     * 检查强制共享/不共享
     * @param $allTimeRange
     * @param $rules
     * @param $adNameMap
     * @throws \Exception
     */
    public static function checkShareOnly($allTimeRange, $rules, $adNameMap)
    {
        if ($allTimeRange) {
            foreach ($allTimeRange as $ad_id => $list) {
                $tempRules = $rules[$ad_id] ?? [];
                $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_ASSIGN_SHARE] ?? [];
                if (empty($tempRules)) continue;
                $tempRules = current($tempRules);
                foreach ($list as $item) {
                    $is_share = $item[4];
                    $message = $tempRules ==  \Doraemon\model\Position\PositionProjectRelations::SHARE_OK ? "强制共享" : "强制不共享";
                    if ($tempRules != $is_share)  throw new \Exception( "[{$adNameMap[$ad_id]}]{$message}");
                }
            }
        }
    }

    /**
     * 检查使用部门数量限制
     * @param $project_id
     * @param $exists
     * @param $allTimeRange
     * @param $departmentId
     * @param $rules
     * @param $adNameMap
     * @throws \Exception
     */
    public static function checkBindLimit($project_id, $exists, $allTimeRange, $departmentId, $rules, $adNameMap)
    {
        $group = [];
        //已经存在的配置处理
        if ($exists) {
            //按照资源位类型分组
            foreach ($exists as $v) {
                $id = $v['id'];
                $ad_id = $v['ad_id'];
                $department_id = $v['department_id'];
                $temp = $allTimeRange[$ad_id] ?? [];
                $tempIds = array_column($temp, '3');
                if (in_array($id, $tempIds)) continue;
                $group[$ad_id][] = [$v['start_show_time'], $v['end_show_time'], 0, $id, $v['is_share'], $department_id, $v['project_id']];
            }
        }
        //新增或修改配置加入已经存在的组
        if ($allTimeRange) {
            foreach ($allTimeRange as $ad_id => $list) {
                foreach ($list as &$tempList) {
                    $tempList[5] = $departmentId;
                }
                $group[$ad_id] = isset($group[$ad_id]) ? array_merge($group[$ad_id], $list) : $list;
            }
        }
        //分组检查使用部门数量限制
        foreach ($group as $ad_id => $list) {
            $compare = $list;
            $tempRules = $rules[$ad_id] ?? [];
            $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_BIND_LIMIT] ?? [];
            if (empty($tempRules)) continue;
            $shareLimit = max($tempRules);
            foreach ($list as $value) {
                if ($value[6] != $project_id) continue;
                $start = $end = $value;
                $start[1] = $start[0];
                $end[0] = $end[1];

                foreach ([$start, $end] as $item) {
                    $debug = [];
                    $debugDepartment = [];
                    //用当前配置的时间范围对比所有配置的时间范围
                    foreach ($compare as $v) {
                        if (($v[3] != 0) && ($v[3] == $item[3])) continue;
                        $temp = array_merge([$item], [$v]);
                        if (self::dateRangeConflict($temp)) {
                            $debug[] = $temp;
                            $debugDepartment[] = $v[5];
                            $debugDepartment[] = $item[5];
                            $debugDepartment = array_flip(array_flip($debugDepartment));
                            if (count($debugDepartment) > $shareLimit) {
                                Log::debug('检查使用部门数量限制debug', compact('debug', 'debugDepartment', 'shareLimit'));
                                throw new \Exception( "[{$adNameMap[$ad_id]}]最多支持同时有{$shareLimit}个使用部门");
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * 检查共享数量限制
     * @param $project_id
     * @param $exists
     * @param $rules
     * @param $allTimeRange
     * @param $adNameMap
     * @throws \Exception
     */
    public static function checkShareLimit($project_id, $exists, $rules, $allTimeRange, $adNameMap)
    {
        $group = [];
        if ($exists) {
            //按照资源位类型分组
            foreach ($exists as $v) {
                $id = $v['id'];
                $ad_id = $v['ad_id'];
                $temp = $allTimeRange[$ad_id] ?? [];
                $tempIds = array_column($temp, '3');
                if (in_array($id, $tempIds)) continue;
                $group[$ad_id][] = [$v['start_show_time'], $v['end_show_time'], 0, $id, $v['is_share'], $ad_id, $v['project_id']];
            }
        }

        if ($allTimeRange) {
            foreach ($allTimeRange as $ad_id => $list) {
                $group[$ad_id] = isset($group[$ad_id]) ? array_merge($group[$ad_id], $list) : $list;
            }
        }

        foreach ($group as $ad_id => $list) {
            $compare = $list;
            sort($compare);
            $tempRules = $rules[$ad_id] ?? [];
            $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_SHARE_LIMIT] ?? [];
            //没有规则限制跳过
            if (empty($tempRules)) continue;
            $shareLimit = max($tempRules);
            foreach ($list as $value) {
                if ($value[6] != $project_id) continue;

                if (self::conflict($value, $compare) > $shareLimit) {
                    Log::position_info('共享数量debug conflict', compact('ad_id', 'value', 'compare'));
                    throw new \Exception( "[{$adNameMap[$ad_id]}]最多支持{$shareLimit}共享数量");
                }

            }
        }
    }

    /**
     * 检查同一时间的冲突
     * @param $project_id
     * @param $exists
     * @param $rules
     * @param $allTimeRange
     * @param $adNameMap
     * @throws \Exception
     */
    public static function checkTimeConflict($project_id, $exists, $rules, $allTimeRange, $adNameMap)
    {
        $combine = [];
        $group = [];
        $transMap = [];
        $transNameMap = [];
        if ($exists) {
            //按照资源位类型分组
            foreach ($exists as $v) {
                $id = $v['id'];
                $ad_id = $v['ad_id'];
                $tempRules = $rules[$ad_id] ?? [];
                $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_CONFLICT] ?? [];
                //没有规则限制跳过
                if (empty($tempRules)) continue;
                //解析规则
                $ad_ids = self::decodeConflictRules([$ad_id], $tempRules);
                $temp = $allTimeRange[$ad_id] ?? [];
                $tempIds = array_column($temp, '3');
                if (in_array($id, $tempIds)) continue;
                //规整到第一个id
                $transId = current($ad_ids);
                $transMap[$ad_id] = $transId;
                $transNameMap[$ad_id] = implode(',', array_map(function($v) use ($adNameMap) {return $adNameMap[$v];}, $ad_ids));
                if ($ad_id == $transId) {
                    $group[$ad_id][] = [$v['start_show_time'], $v['end_show_time'], 0, $id, $v['is_share'], $ad_id, $v['project_id']];
                } else {
                    $combine[$transId][] = [$v['start_show_time'], $v['end_show_time'], 0, $id, $v['is_share'], $transId, $v['project_id']];
                }
            }
        }

        if ($allTimeRange) {
            foreach ($allTimeRange as $ad_id => $list) {
                $tempRules = $rules[$ad_id] ?? [];
                $tempRules = $tempRules[MPositionAdRule::RULE_TYPE_CONFLICT] ?? [];
                //没有规则限制跳过
                if (empty($tempRules)) continue;
                //解析规则
                $ad_ids = self::decodeConflictRules([$ad_id], $tempRules);
                //规整到第一个id
                $transId = current($ad_ids);
                $transMap[$ad_id] = $transId;
                if ($ad_id == $transId) {
                    $group[$ad_id] = isset($group[$ad_id]) ? array_merge($group[$ad_id], $list) : $list;
                } else {
                    $combine[$transId] = isset($combine[$transId]) ? array_merge($combine[$transId], $list) : $list;
                }
            }
        }

        // [开始时间,结束时间,排序,数据表id,分享,类型,项目id]
        foreach ($group as $ad_id => $list) {
            $transId = $transMap[$ad_id] ?? 0;
            //没有规则限制跳过
            if (empty($transId)) continue;
            $compare = $combine[$transId];
            if (empty($compare)) continue;
            sort($compare);
            foreach ($list as $value) {
                if (self::otherConflict($value, $compare)) throw new \Exception( "[{$transNameMap[$ad_id]}]资源位同期占位互斥");
            }
        }
    }

    /**
     * 解析[资源位互斥]规则
    */
    public static function decodeConflictRules($adIds = [], $rules = [])
    {
        foreach ($rules as $rule) {
            $adIds = array_values(array_unique(array_merge($adIds, json_decode($rule))));
        }
        sort($adIds);
        return $adIds;
    }

    public static function otherConflict($compare, $compareList): bool
    {
        $source = $compare;
        while ($count = count($compareList)) {
            $i = 1;
            $compare = $source;
            foreach ($compareList as $v) {
                if ($compare[5] == $v[5]) continue;
                if (self::dateRangeConflict([$compare, $v])) {
                    $compare[0] = max($compare[0], $v[0]);
                    $compare[1] = min($compare[1], $v[1]);
                    $i ++;
                    if ($i > 1) return true;
                }
            }
            array_shift($compareList);
        }
        return false;
    }

    public static function conflict($compare, $compareList): int
    {
        $arr = [];
        $source = $compare;
        while ($count = count($compareList)) {
            $i = 1;
            $compare = $source;
            foreach ($compareList as $v) {
                if (($compare[3] != 0) && ($compare[3] == $v[3])) continue;
                if (($compare[3] == 0) && ($compare[2] == $v[2])) continue;
                if (self::dateRangeConflict([$compare, $v])) {
                    Log::position_info('共享数量debug count', compact('compare', 'v', 'count', 'i'));
                    $compare[0] = max($compare[0], $v[0]);
                    $compare[1] = min($compare[1], $v[1]);
                    $i ++;
                }
            }
            $arr[] = $i;
            array_shift($compareList);
        }

        return max($arr);
    }


    /**
     * 创建资源位占位
     * [
     *      ['2020-11-01', '2020-11-01', 'id'],
     *      ['2020-11-01', '2020-11-01', 'id'],
     *      ['2020-11-01', '2020-11-01', 'id'],
     * ]
     * @param     $dateRangeArr
     * @return array
     */
    public static function dateRangeConflict($dateRangeArr)
    {
        //把时间段进行排序,时间轴上从小到大排列
        sort($dateRangeArr);

        foreach ($dateRangeArr as $k => $v) {
            $i = $k + 1;
            //较小时间段的结束日期 > 较大时间段的开始日期大,那么日期有冲突
            if (isset($dateRangeArr[$i]) && $v[1] >= $dateRangeArr[$i][0]) {
                return [$v[2], $dateRangeArr[$i][2]];
            }
        }
    }
}